#pragma once

#include <ppapi/cpp/instance.h>

#include <ppapi/cpp/graphics_2d.h>
#include <ppapi/cpp/image_data.h>
#include <ppapi/cpp/rect.h>
#include <ppapi/cpp/size.h>
#include <pthread.h>
#include <vector>

#include "scene.h"
#include "vector3.h"

namespace raytrace {

class Primitive;
class Ray;
class RaytraceScriptObject;

class Raytrace : public pp::Instance {
 public:
  explicit Raytrace(PP_Instance instance);
  virtual ~Raytrace();

  virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);

  virtual void DidChangeView(const pp::Rect& position,
                             const pp::Rect& clip);

  virtual pp::Var GetInstanceObject();
  
  // property accessors
  bool GetScene(pp::Var& scene);
  bool SetScene(const pp::Var& scene);

  bool GetMode(pp::Var& mode);
  bool SetMode(const pp::Var& mode);

  bool GetAa(pp::Var& aa);
  bool SetAa(const pp::Var& aa);

  bool GetNumRays(pp::Var& num_rays);
  bool SetNumRays(const pp::Var& num_rays);

  bool quit() const { return quit_; }

  int width() const {
    return pixel_buffer_ ? pixel_buffer_->size().width() : 0;
  }
  int height() const {
    return pixel_buffer_ ? pixel_buffer_->size().height() : 0;
  }

  // Indicate whether a flush is pending.  This can only be called from the
  // main thread; it is not thread safe.
  bool flush_pending() const { return flush_pending_; }
  void set_flush_pending(bool flag) { flush_pending_ = flag; }

 private:

  pp::Var Paint();
  pp::Var Start();
  pp::Var Stop();

  void CreateContext(const pp::Size& size);
  void DestroyContext();

  void CreateScene();

  void FlushPixelBuffer();

  bool IsContextValid() const { return graphics_2d_context_ != NULL; }
  
  mutable pthread_mutex_t pixel_buffer_mutex_;
  mutable pthread_mutex_t raw_pixel_buffer_mutex_;

  RaytraceScriptObject* script_object_;
  pp::Graphics2D* graphics_2d_context_;
  pp::ImageData* pixel_buffer_;
  Vector3* raw_pixel_buffer_;
  int current_row_;
  float percent_complete_;
  bool flush_pending_;
  bool quit_;

  uint32_t aa_;
  uint32_t num_rays_;

  Vector3 cam_position_;
  Vector3 cam_direction_;
  Scene scene_;
  std::string scene_name_;

  pthread_t raytrace_thread_;

  static void* DoRaytrace(void* param);
  void DoRaytraceImpl();
};

}
